package org.jboss.seam.wiki.preferences.metamodel;

import org.apache.log4j.Logger;
import org.jboss.seam.wiki.preferences.PreferenceVisibility;
import org.jboss.seam.wiki.preferences.annotations.Preferences;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import java.lang.annotation.Annotation;
import java.util.*;

@Component("preferenceRegistry")
public class PreferenceRegistry implements InitializingBean, ApplicationContextAware {

    private static final Logger log = Logger.getLogger(PreferenceRegistry.class);

    Set<PreferenceEntity> preferenceEntities = new HashSet<PreferenceEntity>();
    Map<String, PreferenceEntity> preferenceEntitiesByName = new HashMap<String, PreferenceEntity>();

    Set<PreferenceEntity> preferenceEntitiesSystem = new HashSet<PreferenceEntity>();
    Set<PreferenceEntity> preferenceEntitiesUser = new HashSet<PreferenceEntity>();
    Set<PreferenceEntity> preferenceEntitiesInstance = new HashSet<PreferenceEntity>();

	private ApplicationContext applicationContext;

    public void startup() {
        log.debug("initializing preferences registry");

        Set<Class<?>> preferencesClasses = getAnnotatedClasses(Preferences.class);

        if (preferencesClasses == null)
            throw new RuntimeException("No preference entities found, add @Preferences annotation to META-INF/seam-deployment.properties");

        for (Class<?> preferencesClass : preferencesClasses) {
            PreferenceEntity preferenceEntity = new PreferenceEntity(preferencesClass);

            log.debug("adding '" + preferenceEntity.getEntityName() + "', " + preferenceEntity);

            if (preferenceEntitiesByName.containsKey(preferenceEntity.getEntityName())) {
                throw new RuntimeException("Duplicate preference entity name: " + preferenceEntity.getEntityName());
            }

            preferenceEntities.add(preferenceEntity);
            preferenceEntitiesByName.put(preferenceEntity.getEntityName(), preferenceEntity);

            if (preferenceEntity.isSystemPropertiesVisible())
                preferenceEntitiesSystem.add(preferenceEntity);
            if (preferenceEntity.isUserPropertiesVisible())
                preferenceEntitiesUser.add(preferenceEntity);
            if (preferenceEntity.isInstancePropertiesVisible())
                preferenceEntitiesInstance.add(preferenceEntity);
        }

        log.info("registered preference entities: " + preferenceEntities.size());

    }

    public Set<Class<?>> getAnnotatedClasses(Class<? extends Annotation> class1) {
    	Map<String, Object> map = applicationContext.getBeansWithAnnotation(class1);
		Set<Class<?>> set = new HashSet<Class<?>>();
		if(null != map) {
			for(Object key : map.values()) {
				set.add(key.getClass());
			}
		}
//		set.addAll(map.values());
		return set;
	}

	public Set<PreferenceEntity> getPreferenceEntities() {
        return preferenceEntities;
    }

    public Map<String, PreferenceEntity> getPreferenceEntitiesByName() {
        return preferenceEntitiesByName;
    }

    public Set<PreferenceEntity> getPreferenceEntitiesSystem() {
        return preferenceEntitiesSystem;
    }

    public Set<PreferenceEntity> getPreferenceEntitiesUser() {
        return preferenceEntitiesUser;
    }

    public Set<PreferenceEntity> getPreferenceEntitiesInstance() {
        return preferenceEntitiesInstance;
    }

    public SortedSet<PreferenceEntity> getPreferenceEntities(PreferenceVisibility... visibilities) {
        if (visibilities == null) return null;
        SortedSet<PreferenceEntity> entities = new TreeSet<PreferenceEntity>();
        List<PreferenceVisibility> visibilityList = Arrays.asList(visibilities);
        if (visibilityList.contains(PreferenceVisibility.SYSTEM)) entities.addAll(getPreferenceEntitiesSystem());
        if (visibilityList.contains(PreferenceVisibility.USER)) entities.addAll(getPreferenceEntitiesUser());
        if (visibilityList.contains(PreferenceVisibility.INSTANCE)) entities.addAll(getPreferenceEntitiesInstance());
        return entities;
    }

	public void afterPropertiesSet() throws Exception {
		startup();		
	}

	public void setApplicationContext(ApplicationContext applicationContext)
			throws BeansException {
		this.applicationContext = applicationContext;
	}

}